package qyWechat

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"time"
	"ulcdkey/qyWechat/encryptor"
)

const (
	accessTokenUrl = "https://qyapi.weixin.qq.com/cgi-bin/gettoken"
	userListUrl    = "https://qyapi.weixin.qq.com/cgi-bin/user/list"
	userInfoUrl    = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo"
	uploadImage    = "https://qyapi.weixin.qq.com/cgi-bin/media/upload"
)

type QyWechat struct {
	corpId         string
	corpSecret     string
	AgentId        int
	token          string
	encodingAESKey string

	accessToken string
	expiresIn   int64

	encryptor *encryptor.WXBizMsgCrypt
	route     *Router
	Robot     *Robot

	errHandler     func(error, Context) error
	defaultHandler Handler
}
type Handler func(Context) error

func New(corpId, corpSecret, token, encodingAESKey string, agentId int) (w *QyWechat) {
	w = &QyWechat{
		corpId:     corpId,
		corpSecret: corpSecret,
		AgentId:    agentId,

		encryptor: encryptor.NewWXBizMsgCrypt(token, encodingAESKey, corpId, encryptor.XmlType),
		route:     NewRouter(),
		Robot:     NewRobot(),
		errHandler: func(err error, c Context) error {
			return c.Response().Success()
		},

		defaultHandler: func(c Context) error {
			return c.Response().Success()
		},
	}

	return
}

func (w *QyWechat) httpGetAccessToken() (token string, err error) {
	data := url.Values{}
	data.Add("corpid", w.corpId)
	data.Add("corpsecret", w.corpSecret)
	r, err := http.Get(accessTokenUrl + "?" + data.Encode())
	if err != nil {
		return
	}
	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return
	}
	var m = make(map[string]interface{})
	err = json.Unmarshal(b, &m)
	if err != nil {
		return
	}
	if m["errcode"].(float64) != 0 {
		return "", errors.New(m["errmsg"].(string))
	}
	w.accessToken = m["access_token"].(string)
	w.expiresIn = time.Now().Unix() + int64(m["expires_in"].(float64))
	return w.accessToken, err
}

// 获取企业微信的access_token
func (w *QyWechat) getAccessToken() (token string, err error) {
	if w.accessToken != "" && time.Now().Unix() < w.expiresIn {
		return w.accessToken, err
	}
	return w.httpGetAccessToken()
}

// 获取企业部门的成员
type UserListResponse struct {
	ErrCode  int    `json:"errcode"`
	ErrMsg   string `json:"errmsg"`
	UserList []User `json:"userlist"`
}
type User struct {
	UserId      string `json:"userid"`
	Name        string `json:"name"`
	Department  []int  `json:"department"`
	Position    string `json:"position"`
	Mobile      string `json:"mobile"`
	Gender      string `json:"gender"`
	Avatar      string `json:"avatar"`
	ThumbAvatar string `json:"thumb_avatar"`
	Status      int    `json:"status"`
	OpenUserId  string `json:"open_userid"`
}

func (w *QyWechat) GetUserList(departmentId int) (us []User, err error) {
	data := url.Values{}
	token, err := w.getAccessToken()
	if err != nil {
		return
	}
	data.Add("access_token", token)
	data.Add("department_id", fmt.Sprint(departmentId))
	data.Add("fetch_child", "1") // 递归
	r, err := http.Get(userListUrl + "?" + data.Encode())
	if err != nil {
		return
	}
	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return
	}
	var m UserListResponse
	err = json.Unmarshal(b, &m)
	if err != nil {
		return
	}
	if m.ErrCode != 0 {
		if m.ErrCode == 40014 || m.ErrCode == 42001 {
			w.httpGetAccessToken()
		}
		return us, errors.New(m.ErrMsg)
	}
	us = m.UserList
	return
}

// 上传图片
type MediaUpload struct {
	ErrCode   int    `json:"errcode"`
	ErrMsg    string `json:"errmsg"`
	Type      string `json:"type"`
	MediaID   string `json:"media_id"`
	CreatedAt string `json:"created_at"`
}

func (w *QyWechat) UploadImage(bs []byte) (mediaId string, err error) {
	data := url.Values{}
	token, err := w.getAccessToken()
	if err != nil {
		return
	}
	data.Add("access_token", token)
	data.Add("type", "image")
	//file, err := os.Open(imagePath)
	//if err != nil {
	//	return
	//}
	//defer file.Close()
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile("image", fmt.Sprint(time.Now().Unix())+".png")
	if err != nil {
		return
	}
	_, err = part.Write(bs)
	if err != nil {
		return
	}
	//_, err = io.Copy(part, file)
	err = writer.Close()
	if err != nil {
		return
	}

	req, err := http.NewRequest("POST", uploadImage+"?"+data.Encode(), body)
	req.Header.Add("Content-Type", writer.FormDataContentType())
	client := http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return
	}
	defer res.Body.Close()
	b, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return
	}
	var media MediaUpload
	err = json.Unmarshal(b, &media)
	if err != nil {
		return
	}
	if media.ErrCode != 0 {
		if media.ErrCode == 40014 || media.ErrCode == 42001 {
			w.httpGetAccessToken()
		}
		return "", errors.New(media.ErrMsg)
	}
	mediaId = media.MediaID
	return
}

// 获取授权用户的身份
func (w *QyWechat) GetUserInfo(code string) (userId string, err error) {
	token, err := w.getAccessToken()
	if err != nil {
		return
	}
	data := url.Values{}
	data.Add("access_token", token)
	data.Add("code", code)
	r, err := http.Get(userInfoUrl + "?" + data.Encode())
	if err != nil {
		return
	}
	defer r.Body.Close()
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return
	}
	var m = make(map[string]interface{})
	err = json.Unmarshal(b, &m)
	if err != nil {
		return
	}
	if m["errcode"].(float64) != 0 {
		if m["errcode"].(float64) == 40014 || m["errcode"].(float64) == 42001 {
			w.httpGetAccessToken()
		}
		return "", errors.New(m["errmsg"].(string))
	}
	userId = m["UserId"].(string)
	return
}

/* 验证 */
func (w *QyWechat) VerifyURL(msgSignature, timestamp, nonce, echoStr string) (outStr []byte, err error) {
	outStr, e := w.encryptor.VerifyURL(msgSignature, timestamp, nonce, echoStr)
	if e != nil {
		err = errors.New(e.ErrMsg)
	}
	return
}

/* 加密 */
func (w *QyWechat) EncryptMsg(toMsg, timestamp, nonce string) ([]byte, error) {
	b, e := w.encryptor.EncryptMsg(toMsg, timestamp, nonce)
	if e != nil {
		return b, errors.New(e.ErrMsg)
	}

	return b, nil
}

/* 解密 */
func (w *QyWechat) DecryptMsg(msgSignature, timestamp, nonce string, fromMsg []byte) ([]byte, error) {
	b, e := w.encryptor.DecryptMsg(msgSignature, timestamp, nonce, fromMsg)
	if e != nil {
		return b, errors.New(e.ErrMsg)
	}
	return b, nil
}

// 接口服务（接收企业微信发来的消息）
func (w *QyWechat) Server(rw http.ResponseWriter, r *http.Request) {
	msgSignature := r.URL.Query().Get("msg_signature")
	timestamp := r.URL.Query().Get("timestamp")
	nonce := r.URL.Query().Get("nonce")
	if r.Method == "GET" {
		echoStr := r.URL.Query().Get("echostr")
		outStr, err := w.VerifyURL(msgSignature, timestamp, nonce, echoStr)
		if err != nil {
			log.Println("url验证失败:", err)
		}
		rw.Write(outStr)
		return
	}

	if r.Method == "POST" {
		c, err := newContext(rw, r, w)
		if err != nil {
			if h := w.errHandler; h != nil {
				h(err, c)
			}
			return
		}

		h := w.findHandler(c)
		if h == nil {
			h = w.defaultHandler
		}
		if err = h(c); err != nil {
			w.errHandler(err, c)
		}
		return
	}

}

func (w *QyWechat) Add(where map[string]string, h Handler) {
	w.route.Add(where, h)
}

func (w *QyWechat) findHandler(c Context) Handler {
	return w.route.FindHandler(c)
}
